/*
 * Copyright (C) 2019 Peng fei Pan <panpfpanpf@outlook.me>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package me.panpf.sketch.viewfun;


import me.panpf.sketch.DrawableImage;
import me.panpf.sketch.PixelMapDrawable;
import me.panpf.sketch.SketchView;
import me.panpf.sketch.drawable.MiPixmapElement;
import me.panpf.sketch.drawable.SketchLoadingDrawable;
import me.panpf.sketch.request.DisplayCache;
import me.panpf.sketch.request.DisplayListener;
import me.panpf.sketch.request.DisplayOptions;
import me.panpf.sketch.request.DownloadProgressListener;
import me.panpf.sketch.uri.UriModel;
import me.panpf.sketch.util.Log;
import me.panpf.sketch.zoom.PixmapBuild;
import me.panpf.sketch.zoom.scroller.GestureDetector;
import ohos.agp.animation.Animator;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.Component.ClickedListener;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.Image;
import ohos.agp.components.element.Element;
import ohos.agp.components.element.PixelMapElement;
import ohos.agp.render.Canvas;
import ohos.app.Context;
import ohos.global.resource.ResourceManager;
import ohos.media.image.ImageSource;
import ohos.media.image.PixelMap;
import ohos.multimodalinput.event.TouchEvent;
import ohos.utils.net.Uri;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.List;

/**
 * 这个类负责给 function 回调各种状态
 */
public abstract class FunctionCallbackView extends DrawableImage implements SketchView,
        Component.TouchEventListener, Component.EstimateSizeListener,
        Component.BindStateChangedListener, Component.DrawTask {

    ClickedListener wrappedClickListener;
    LongClickedListener longClickListener;
    DisplayListener wrappedDisplayListener;
    DownloadProgressListener wrappedProgressListener;
    Element savedElement;
    PixmapBuild pixmapBuild;

    private volatile ViewFunctions functions;
    private ProgressListenerProxy progressListenerProxy;
    private DisplayListenerProxy displayListenerProxy;
    private OnClickListenerProxy clickListenerProxy;
    private ImageSource imageSource;
    private String uri;
    private GestureDetector detector;
    private String imageType;

    public FunctionCallbackView(Context context) {
        this(context, null, 0);
    }

    public FunctionCallbackView(Context context, AttrSet attrs) {
        this(context, attrs, 0);
    }

    public FunctionCallbackView(Context context, AttrSet attrs, int defStyle) {
        super(context, attrs, "");
        displayListenerProxy = new DisplayListenerProxy(this);
        progressListenerProxy = new ProgressListenerProxy(this);
        clickListenerProxy = new OnClickListenerProxy(this);
        detector = new GestureDetector(getContext(), new Listener());

//        super.setClickedListener(clickListenerProxy);
        updateClickable();

        setTouchEventListener(this);
        setEstimateSizeListener(this);
        setBindStateChangedListener(this);
//        addDrawTask(this);
    }

    ViewFunctions getFunctions() {
        /* 为什么要搞成延迟 new 的？因为父类会第一时间调用 setDrawable() 方法，
        这个方法里需要用到 functions，即使直接 ViewFunctions functions = new ViewFunctions(this); 都不行  */
        if (functions == null) {
            synchronized (this) {
                if (functions == null) {
                    functions = new ViewFunctions(this);
                }
            }
        }
        return functions;
    }

    @Override
    public void arrange(int left, int top, int width, int height) {
        super.arrange(left, top, width, height);
        getFunctions().onLayout(true, left, top, left + width, top + height);
    }

//    @Override
//    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
//        super.onLayout(changed, left, top, right, bottom);
//
//        getFunctions().onLayout(changed, left, top, right, bottom);
//    }

//    @Override
//    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
//        super.onSizeChanged(w, h, oldw, oldh);
//
//        getFunctions().onSizeChanged(w, h, oldw, oldh);
//    }


    public PixmapBuild getPixmapBuild() {
        return pixmapBuild;
    }

    @Override
    public Element getDrawable() {
        return getImageElement();
    }

    @Override
    public Element getImageElement() {
        if (savedElement != null) {
            return savedElement;
        }
        return super.getImageElement();
    }


    @Override
    public ScaleMode getScaleType() {
        return getScaleMode();
    }


    @Override
    public void clearAnimation() {
    }

    @Override
    public void startAnimation(Animator animation) {
    }

    @Override
    public ComponentContainer.LayoutConfig getLayoutParams() {
        return getLayoutConfig();
    }

    @Override
    public ResourceManager getResources() {
        return getResourceManager();
    }

    @Override
    public boolean onEstimateSize(int i, int i1) {
        return false;
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        super.onDraw(component, canvas);
        getFunctions().onDraw(canvas);
    }

    @Override
    public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
        detector.onTouchEvent(touchEvent);
        boolean handled = getFunctions().onTouchEvent(touchEvent);
        // 手指按下时一定要返回true, 不然后续事件无法进来
        return touchEvent.getAction() == TouchEvent.PRIMARY_POINT_DOWN || handled;
    }

    @Override
    public void onComponentBoundToWindow(Component component) {
        getFunctions().onAttachedToWindow();
    }

    @Override
    public void onComponentUnboundFromWindow(Component component) {
        if (getFunctions().onDetachedFromWindow()) {
            setImageElement((Element) null);
        }
    }

    @Override
    public void setScaleMode(ScaleMode scaleMode) {
        ImageZoomFunction zoomFunction = getFunctions().getZoomFunction();
        if (zoomFunction != null && zoomFunction.getZoomer().isWorking() && scaleMode != ScaleMode.STRETCH) {
            zoomFunction.setScaleType(scaleMode);
        } else {
            super.setScaleMode(scaleMode);
        }
    }

    public ClickedListener getOnClickListener() {
        return clickListenerProxy;
    }


    @Override
    public void setClickedListener(ClickedListener listener) {
//        super.setClickedListener(listener);
        wrappedClickListener = listener;
        updateClickable();
    }

    public LongClickedListener getLongClickListener() {
        return longClickListener;
    }

    public void setLongClickListener(LongClickedListener longClickListener) {
        this.longClickListener = longClickListener;
        setLongClickedListener(longClickListener);
    }

    void updateClickable() {
        setClickable(clickListenerProxy.isClickable());
    }

//    @Override
//    public void setImageURI( Uri uri) {
//        final Element oldDrawable = getDrawable();
//        super.setImageURI(uri);
//        final Element newDrawable = getDrawable();
//
//        setDrawable("setImageURI", oldDrawable, newDrawable);
//    }


    @Override
    public void setImageAndDecodeBounds(int resId) {
        final Element oldDrawable = getDrawable();
        super.setImageAndDecodeBounds(resId);
        final Element newDrawable = getDrawable();

        setDrawable("setImageResource", oldDrawable, newDrawable);
    }


    @Override
    public void setImageElement(Element element) {
        if (element != null) {
            if (element instanceof MiPixmapElement) {
                pixmapBuild = ((MiPixmapElement) element).getPixmapBuild();
            }
        }
        super.setImageElement(element);
    }

    public void setImageElement(PixmapBuild imageSource) {
        if (imageSource != null) {
            this.pixmapBuild = imageSource;
            super.setPixelMap(pixmapBuild.getPixelMap());
        }
    }

    @Override
    public void setImageDrawable(Element drawable) {
        savedElement = null;
        setImageElement((Element) null);
        setPixelMap(null);

        final Element oldDrawable = getDrawable();
//        setImageElement(drawable);
        if (drawable instanceof PixelMapDrawable) {
            ((PixelMapDrawable) drawable).setDrawableCallback(this);
            savedElement = drawable;
            invalidate();
        } else if (drawable instanceof MiPixmapElement) {
            savedElement = drawable;
            MiPixmapElement pme = (MiPixmapElement) drawable;
//             这里需要setPixelMap而不是setImageElement, setImageElement不能应用image的scaleType, setPixelMap可以
            setPixelMap(pme.getPixelMap());
        } else {
            setImageElement(drawable);
        }

        final Element newDrawable = getDrawable();
        setDrawable("setImageDrawable", oldDrawable, newDrawable);
    }

    private void setDrawable(String callPosition, Element oldDrawable, Element newDrawable) {
        if (newDrawable == null) {
            getFunctions().requestFunction.clean();
        }
        if (oldDrawable != newDrawable) {
            if (getFunctions().onDrawableChanged(callPosition, oldDrawable, newDrawable)) {
                invalidate();
            }
        }
    }

    @Override
    public void onReadyDisplay(UriModel uriModel) {
        if (getFunctions().onReadyDisplay(uriModel)) {
            invalidate();
        }
    }

    @Override
    public DisplayOptions getOptions() {
        return getFunctions().requestFunction.getDisplayOptions();
    }

    @Override
    public void setOptions(DisplayOptions newDisplayOptions) {
        if (newDisplayOptions == null) {
            getFunctions().requestFunction.getDisplayOptions().reset();
        } else {
            getFunctions().requestFunction.getDisplayOptions().copy(newDisplayOptions);
        }
    }

    @Nullable
    @Override
    public DisplayListener getDisplayListener() {
        return displayListenerProxy;
    }

    @Override
    public void setDisplayListener(DisplayListener displayListener) {
        this.wrappedDisplayListener = displayListener;
    }

    @Nullable
    @Override
    public DownloadProgressListener getDownloadProgressListener() {
        if (getFunctions().showDownloadProgressFunction != null || wrappedProgressListener != null) {
            return progressListenerProxy;
        } else {
            return null;
        }
    }

    @Override
    public void setDownloadProgressListener(DownloadProgressListener downloadProgressListener) {
        this.wrappedProgressListener = downloadProgressListener;
    }

    @Nullable
    @Override
    public DisplayCache getDisplayCache() {
        return getFunctions().requestFunction.getDisplayCache();
    }

    @Override
    public void setDisplayCache(DisplayCache displayCache) {
        getFunctions().requestFunction.setDisplayCache(displayCache);
    }

    @Override
    public boolean isUseSmallerThumbnails() {
        return isZoomEnabled();
    }

    @Override
    public void setImageSource(ImageSource imageSource) {
        this.imageSource = imageSource;
    }

    public ImageSource getImageSource() {
        return imageSource;
    }

    @Override
    public void setImageUri(String uri) {
        this.uri = uri;
    }

    public String getUri() {
        return uri;
    }

    @Override
    public void setImageType(String imageType) {
        this.imageType = imageType;
    }

    public String getImageType() {
        return imageType;
    }

    class Listener extends GestureDetector.SimpleOnGestureListener {
        @Override
        public boolean onSingleTapUp(TouchEvent touchEvent) {
            if (isClickable()) {
                clickListenerProxy.onClick(FunctionCallbackView.this);
            }
            return super.onSingleTapUp(touchEvent);
        }

        @Override
        public void onLongPress(TouchEvent touchEvent) {
            if (longClickListener != null) {
                longClickListener.onLongClicked(FunctionCallbackView.this);
            }
            super.onLongPress(touchEvent);
        }
    }
}
